package com.basic.ui.view

import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.graphics.Rect
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams
import android.view.ViewGroup.MarginLayoutParams
import androidx.core.view.*
import com.basic.util.nullOr

/**
 * used for layout item view int viewgroup such as listview, recycleview, viewpager...
 */
typealias Itemflater<T> = ViewGroup.(item: T, position: Int) -> View

/**
 * used for layout item view by layout resource int viewgroup such as listview, recycleview, viewpager...
 */
typealias ItemLayoutflater<T> = ViewGroup.(layoutId: Int, item: T, position: Int) -> View

fun ViewGroup.inflate(layoutId: Int, attach: Boolean): View {
    return LayoutInflater.from(context).inflate(layoutId, this, attach)
}

fun View.runOrPost(condition: Boolean, runnable: Runnable) {
    if (condition) {
        runnable.run()
    } else {
        post(runnable)
    }
}

fun Context.getActivity(): Activity? {
    var context = this
    if (context is Activity) {
        return context
    }
    while (context is ContextWrapper) {
        context = context.baseContext
        if (context is Activity) {
            return context
        }
    }
    return null
}

fun View.getActivity(): Activity? {
    return this.context.getActivity()
}

fun <T : View> T.setVisible(visible: Boolean, visibleAction: (T.() -> Unit)? = null): T {
    if (visible) {
        visibility = View.VISIBLE
        visibleAction?.invoke(this)
    } else {
        visibility = View.GONE
    }
    return this
}

fun <T : View> T.setInVisible(invisible: Boolean, trueAction: (T.() -> Unit)? = null): T {
    if (invisible) {
        visibility = View.INVISIBLE
        trueAction?.invoke(this)
    } else {
        visibility = View.VISIBLE
    }
    return this
}

fun <T : View> T.gone(goneAction: (T.() -> Unit)? = null): T {
    visibility = View.GONE
    goneAction?.invoke(this)
    return this
}

fun View.setDefaultParams(
    w: Int = LayoutParams.WRAP_CONTENT,
    h: Int = LayoutParams.WRAP_CONTENT
): MarginLayoutParams {
    return MarginLayoutParams(w, h).also {
        this.layoutParams = it
    }
}

fun View.updateParams(block: MarginLayoutParams.() -> Unit) {
    if (layoutParams != null) {
        updateLayoutParams<MarginLayoutParams> {
            block(this)
        }
    } else {
        setDefaultParams().block()
    }
}

var View.w: Int
    get() {
        return if (this.isLaidOut) {
            width
        } else if (measuredWidth != 0) {
            measuredWidth
        } else {
            layoutParams?.width ?: 0
        }
    }
    set(value) {
        updateParams {
            width = value
        }
    }

var View.h: Int
    get() {
        return if (this.isLaidOut) {
            height
        } else if (measuredHeight != 0) {
            measuredHeight
        } else {
            layoutParams?.height ?: 0
        }
    }
    set(value) {
        updateParams {
            height = value
        }
    }

var View.startMargin: Int
    get():Int {
        return marginStart
    }
    set(value) {
        updateParams {
            marginStart = value
        }
    }

var View.topMargin: Int
    get():Int {
        return marginTop
    }
    set(value) {
        updateParams {
            topMargin = value
        }
    }

var View.endMargin: Int
    get():Int {
        return marginEnd
    }
    set(value) {
        updateParams {
            marginEnd = value
        }
    }

var View.bottomMargin: Int
    get():Int {
        return marginBottom
    }
    set(value) {
        updateParams {
            bottomMargin = value
        }
    }

var View.bottomPadding: Int
    get(): Int {
        return paddingBottom
    }
    set(value) {
        if (value != paddingBottom) {
            setPadding(paddingLeft, paddingTop, paddingRight, value)
        }
    }

var View.topPadding: Int
    get(): Int {
        return paddingTop
    }
    set(value) {
        if (value != paddingTop) {
            setPadding(paddingLeft, value, paddingRight, paddingBottom)
        }
    }

var View.startPadding: Int
    get(): Int {
        return paddingStart
    }
    set(value) {
        if (value != paddingStart) {
            setPadding(value, paddingTop, paddingEnd, paddingBottom)
        }
    }

var View.endPadding: Int
    get(): Int {
        return paddingEnd
    }
    set(value) {
        if (value != paddingEnd) {
            setPadding(paddingStart, paddingTop, value, paddingBottom)
        }
    }

typealias OnItemClick<T> = (view: View, item: T, pos: Int) -> Unit

val View?.isFullVisibleToUser
    get() = getVisibilityStatus().first

val View?.isPartVisibleToUser
    get() = getVisibilityStatus().second


val View?.isInvisibleToUser
    get() = getVisibilityStatus().third

/**
 * @return A Triple contains first for [isFullVisibleToUser], second for
 * [isPartVisibleToUser], and third for [isInvisibleToUser].
 */
internal fun View?.getVisibilityStatus(): Triple<Boolean, Boolean, Boolean> {
    val visible: Boolean = this != null &&
            ViewCompat.isAttachedToWindow(this) && isShown

    if (!visible) {
        return Triple(false, false, true)
    }

    val visibleArea = Rect()
    val partOrFullVisible = this?.getGlobalVisibleRect(visibleArea).nullOr(false)

    val fullHeightVisible = (visibleArea.bottom - visibleArea.top) >=
            this?.height.nullOr(0)
    val fullWidthVisible = (visibleArea.right - visibleArea.left) >=
            this?.width.nullOr(0)

    val fullViewVisible = partOrFullVisible && fullHeightVisible && fullWidthVisible
    val partViewVisible = partOrFullVisible && (!fullHeightVisible || !fullWidthVisible)
    val viewInvisible = !partOrFullVisible

    return Triple(fullViewVisible, partViewVisible, viewInvisible)
}



